home *** CD-ROM | disk | FTP | other *** search
- /** FifoBBS.rexx
- *
- * Test of fifo-handler. A small BBS. Really small...
- *
- * This requires VLT or VLTjr version 5.028 or later, and
- * Matt Dillon's Fifo.library and fifo-handler. Please
- * install these files and mount fifo: before running this.
- *
- * Usage: FifoBBS [local | remote]
- *
- * FifoBBS, when invoked without arguments will run a fake BBS
- * in the current CLI. When invoked with the "local" argument,
- * it will run with a local VLT, bypassing the serial port.
- * In neither of these cases will "UPLOAD" or "DOWNLOAD" work.
- * When invoked with the "remote" argument, it will run as a
- * real BBS, through the serial port.
- *
- * The BBS installs itself almost completely. All you have to
- * do is assign FifoBBS: or change the BBSdevice string later on
- * to the location you want. You will also need to set up VLT
- * for running with its pipes on. After starting the BBS for
- * for the first time, you can log on as Sysop, password
- * SYSOP. It will ask you to change your password. From that
- * moment on, you're in business. People can register, the
- * sysop can validate them. Once on the system, type help to
- * find a list of commands.
- *
- * Alpha 0.4 by W.G.J. Langeveld, 30 January 1991.
- *
- **/
- parse arg action
- /*
- * Allow no interruptions for secure operation
- */
- SIGNAL ON BREAK_C
- SIGNAL ON BREAK_D
- SIGNAL ON BREAK_E
- SIGNAL ON BREAK_F
- SIGNAL ON ERROR
- SIGNAL ON FAILURE
- SIGNAL ON HALT
- SIGNAL ON SYNTAX
- /*
- * This one is really for debugging purposes:
- */
- SIGNAL ON NOVALUE
-
- Options failat 300
- SignalLabel = "Start"
- /*
- * Trick: here are all the global variables we want accessible to all
- * routines. Watch the way interpret is used in the Procedure definitions
- */
- GLOBAL = "GLOBAL SignalLabel BBSdevice BBSusers BBSlistings"
- GLOBAL = GLOBAL || " BBSgeneral BBSmail BBSadmin BBSsysmsg BBSprompt"
- GLOBAL = GLOBAL || " Protocols. CurrentUser."
- /*
- * Get the support library.
- */
- check = addlib('rexxsupport.library', 0, -30, 0)
- /*
- * BBS definitions
- */
- BBSdevice = "FifoBBS:"
- BBSusers = BBSdevice"users"
- BBSlistings = BBSdevice"listings"
- BBSgeneral = BBSdevice"general"
- BBSmail = BBSdevice"mail"
- BBSadmin = BBSdevice"admin"
- BBSsysmsg = BBSdevice"admin/system.msg"
- BBSprompt = "FifoBBS> "
- /*
- * Check if the sections exist, or else make them
- */
- if ~exists(BBSdevice) then do
- say "You must set up an assignment called "BBSdevice
- exit 0
- end
- if ~exists(BBSusers) then call Makedir(BBSusers)
- if ~exists(BBSlistings) then call Makedir(BBSlistings)
- if ~exists(BBSgeneral) then call Makedir(BBSgeneral)
- if ~exists(BBSmail) then call Makedir(BBSmail)
- if ~exists(BBSadmin) then call Makedir(BBSadmin)
- if ~exists(BBSsysmsg) then address command "echo >"BBSsysmsg' "No news"'
- /*
- * If there's no sysop account, make one
- */
- if ~exists(BBSmail"/Sysop") then do
- Tmp. = ""
- Tmp.Account = "Sysop"
- Tmp.Password = "SYSOP"
- Tmp.Access = 5
- Tmp.Name = "Sysop"
- Tmp.MsgCount = 0
- Tmp.MailCount = 0
- Tmp.Protocol = 1
- call SetRecord()
- call Makedir(BBSmail"/Sysop")
- end
- /*
- * Transfer Protocols
- */
- Protocols.0 = 5
- Protocols.1.nam = "XMODEM"
- Protocols.1.lib = "xprxmodem.library"
- Protocols.1.set = "C1,K1"
- Protocols.2.nam = "ZMODEM"
- Protocols.2.lib = "xprzmodem.library"
- Protocols.2.set = "T?,OS,B1,AN,DN,KN,SN,RN"
- Protocols.3.nam = "Kermit"
- Protocols.3.lib = "xprkermit.library"
- Protocols.3.set = "OCY,GN,TN,P1500,B3"
- Protocols.4.nam = "CIS QuickB"
- Protocols.4.lib = "xprquickb.library"
- Protocols.4.set = "TC,OS,B1,AN,DN,KN"
- Protocols.5.nam = "ASCII"
- Protocols.5.lib = "xprascii.library"
- Protocols.5.set = "50"
- /*
- * Redirect I/O to VLT's pipes
- * For use as a BBS, use VLTR (remote). For local tests
- * use VLTL (local).
- */
- if action = "remote" then pip = "VLTR"
- else pip = "VLTL"
- /*
- * When action is not either "local" or "remote", you will run in
- * the CLI (and you will see some echoes not otherwise present).
- */
- if action ~= "" then do
- call close("STDIN")
- call close("STDOUT")
- /*
- * First open fifo for read/write and assign to stdin
- */
- if ~open("STDIN", "fifo:"pip"/rws") then do
- say "Can't open read pipe"
- exit 0
- end
- /*
- * Identify stdin with the "current console"
- */
- call pragma('*', "STDIN")
- /*
- * Open stdout to the current console for write.
- */
- if ~open("STDOUT", '*', "W") then do
- say "Can't open write pipe"
- exit 0
- end
- end
-
-
-
- /*
- * Wait for <cr>. Here's where we go on severe problems.
- */
- Start:
- s = GetCommand("", 0)
- /*
- * Welcome message.
- */
- say "+--------------------------------------------------+"
- say "| FifoBBS - Only authorized users are welcome! +"
- say "+--------------------------------------------------+"
- /*
- * Log in. Don't let users without sufficient privilege get past here.
- */
- CurrentUser. = ""
- call Login()
- if CurrentUser.Access < 3 then interpret "SIGNAL" SignalLabel
- /*
- * If user is Sysop, make sure the password is changed first time
- */
- if upper(CurrentUser.Account) = "SYSOP" then do
- do while upper(CurrentUser.Password) = "SYSOP"
- say "You MUST change the Sysop password now!"
- call ChangePassword()
- end
- end
- /*
- * System message
- */
- if exists(BBSsysmsg) then address command "type "BBSsysmsg
- /*
- * Unread mail
- */
- n = GetMsgLeft(BBSmail"/"CurrentUser.Account, CurrentUser.MailCount)
- if n ~= 0 then say "You have "n" unread mail message"Esses(n)
- /*
- * Unread regular messages
- */
- n = GetMsgLeft(BBSgeneral, CurrentUser.MsgCount)
- if n ~= 0 then say "You have "n" unread general message"Esses(n)
- /*
- * Main loop. Not too many commands yet. But you get the
- * idea... Some commands are only available with level 5 clearance.
- */
- do i = 1
- s = GetCommand(BBSprompt, 1)
- parse var s cmd arg1 arg2 .
- cmd = upper(cmd)
- select
- when abbrev("DOWNLOAD", cmd, 2) then call Download(arg1)
- when abbrev("ENTER", cmd, 3) then call EnterMsg("")
- when abbrev("EXIT", cmd, 4) then call ExitBBS(cmd)
- when abbrev("HELP", cmd, 1) then call HelpList("")
- when abbrev("LIST", cmd, 2) then call ListFiles()
- when abbrev("LOGOFF", cmd, 2) then leave i
- when abbrev("MAIL", cmd, 2) then call DoMail()
- when abbrev("PASSWORD", cmd, 3) then call ChangePassword()
- when abbrev("PROTOCOL", cmd, 3) then call ChangeProtocol(arg1)
- when abbrev("REGISTER", cmd, 3) then call Register(cmd)
- when abbrev("READ", cmd, 2) then call ReadMsg(arg1)
- when abbrev("SHOW", cmd, 2) then call ShowRecord(arg1)
- when abbrev("SYSTEM", cmd, 2) then call DoSystem(cmd)
- when abbrev("UPLOAD", cmd, 2) then call UpLoad(arg1)
- when abbrev("USERS", cmd, 2) then call ShowUsers()
- when abbrev("VALIDATE", cmd, 1) then call Validate(cmd, arg1, arg2)
- otherwise call HelpList(cmd)
- end
- end
-
- /*
- * Save message and mail count
- */
- n = CurrentUser.MsgCount
- m = CurrentUser.MailCount
- Tmp.Account = CurrentUser.Account
- call GetRecord()
- Tmp.MsgCount = n
- Tmp.MailCount = m
- call SetRecord()
- /*
- * Logout
- */
- say CurrentUser.Name" logged off at "time()
- interpret "SIGNAL" SignalLabel
-
-
-
- /**************************************************************/
- /**************** Functions ***********************************/
- /**************************************************************/
-
- /**
- *
- * Change the password
- *
- **/
- ChangePassword: interpret "Procedure Expose" GLOBAL
- Tmp.Account = CurrentUser.Account
-
- if GetRecord() = 1 then do
- t = upper(GetCommand("Old Password: ", 0))
- if t ~= Tmp.Password then do
- say "Invalid Password"
- return
- end
- t = upper(GetCommand("New Password: ", 0))
- u = upper(GetCommand("Verification: ", 0))
- if u ~= t then do
- say "Verification doesn't match new password, aborted"
- return
- end
- else do
- Tmp.Password = u
- CurrentUser.Password = u
- call SetRecord()
- end
- end
- return
-
-
-
- /**
- *
- * Change the transfer protocol
- *
- **/
- ChangeProtocol: interpret "Procedure Expose" GLOBAL
- arg s
-
- if s = "" then do
- say "Transfer Protocol:"
-
- do i = 1 to Protocols.0
- say i". "Protocols.i.nam
- end
- i = CurrentUser.Protocol + 0
- say "Your current protocol is "Protocols.i.nam
- end
-
- Tmp.Account = CurrentUser.Account
-
- if GetRecord() = 1 then do
- do i = 1
- if s = "" then t = upper(GetCommand("Enter new protocol (1 - 5): ", 1))
- else t = s
-
- if (t ~= 1) & (t ~= 2) & (t ~= 3) & (t ~= 4) & (t ~= 5) then do
- say "A number from 1 through 5 was expected"
- s = ""
- iterate i
- end
- leave i
- end
-
- CurrentUser.Protocol = t
- if s = "" then do
- if GetYesNo("Save for next time? ") = 1 then do
- Tmp.Protocol = t
- call SetRecord()
- end
- end
- end
- return
-
-
-
- /**
- *
- * Collect a message
- *
- **/
- CollectMsg: interpret "Procedure Expose" GLOBAL "msg."
- arg comm
-
- say "Enter the message below."
- say "Enter a dot as the first character on a line to exit."
-
- if comm = "" then do
- msg.3 = "Title: " || GetCommand("Title: ", 1)
- ni = 4
- end
- else ni = 3
-
- do k = 1
- do n = ni
- msg.n = GetCommand(">", 1)
- if substr(msg.n, 1, 1) = "." then leave n
- end
-
- do i = 1
- s = upper(GetCommand("Quit, Continue, List, Post: ", 1))
- if abbrev("QUIT", s, 1) then return 0
- else if abbrev("LIST", s, 1) then do
- do j = 3 to n - 1
- say msg.j
- end
- end
- else if abbrev("POST", s, 1) then leave k
- else if abbrev("CONTINUE", s, 1) then do
- ni = n
- leave i
- end
- end
- end
- return n - 1
-
-
- /**
- *
- * Copy the user's record from Tmp.
- *
- **/
- CopyRecord: interpret "Procedure Expose" GLOBAL "Tmp."
- CurrentUser.Account = Tmp.Account
- CurrentUser.Password = Tmp.Password
- CurrentUser.Access = Tmp.Access
- CurrentUser.Name = Tmp.Name
- CurrentUser.City = Tmp.City
- CurrentUser.Country = Tmp.Country
- CurrentUser.Telephone = Tmp.Telephone
- CurrentUser.MsgCount = Tmp.MsgCount
- CurrentUser.MailCount = Tmp.MailCount
- CurrentUser.Protocol = Tmp.Protocol
-
- return
-
-
- /**
- *
- * Mail subsystem. Two commands: read and enter. They use the same
- * basic functions as the main system, but with different paths.
- *
- **/
- DoMail: interpret "Procedure Expose" GLOBAL
- do i = 1
- s = upper(GetCommand("Mail: ", 1))
- parse var s cmd arg1 .
- select
- when abbrev("TO", cmd, 2) then call EnterMail("", arg1)
- when abbrev("HELP", cmd, 1) then call HelpLMail("")
- when abbrev("QUIT", cmd, 1) then leave i
- when abbrev("READ", cmd, 2) then call ReadMail(arg1)
- when abbrev("SHOW", cmd, 2) then call ShowRecord(arg1)
- when abbrev("USERS", cmd, 2) then call ShowUsers()
- otherwise call HelpLMail(cmd)
- end
- end
- return
-
-
-
- /**
- *
- * Download an existing file
- *
- **/
- DownLoad : interpret "Procedure Expose" GLOBAL
- parse arg filnam
-
- if filnam = "" then filnam = GetCommand("File name? ", 1)
- if ~exists(BBSlistings"/"filnam) then do
- say "Can't find file "filnam
- return
- end
-
- say "Get ready to receive file "filnam
-
- proto = CurrentUser.Protocol + 0
- address VLT "transfer protocol external; transfer mode image"
- address VLT "xpr select "Protocols.proto.lib
- address VLT "CD "BBSlistings
- if Protocols.proto.set ~= "" then address VLT "xpr init "Protocols.proto.set
- address VLT "file send "BBSlistings"/"filnam
- /*
- * Switch back to XMODEM protocol so that we can't automatically start
- * receiving stuff.
- */
- address VLT "transfer protocol XMODEM"
- return
-
-
-
- /**
- *
- * More or less direct access to the system
- *
- **/
- DoSystem: interpret "Procedure Expose" GLOBAL
- parse arg s
- if CurrentUser.Access < 5 then do
- call HelpList(s)
- return
- end
- /*
- * In case a command breaks, this is the label we want to get
- * back to.
- */
- SysCall: SignalLabel = "SysCall"
- do i = 1
- s = GetCommand("$ ", 1)
- parse var s cmd rest
- cmd = upper(cmd)
- if abbrev("RETURN", cmd, 3) then do
- leave i
- end
- else if (cmd = "CD") & (rest ~= "") then do
- call pragma("Directory", strip(rest))
- end
- else do
- address command s
- end
- end
- /*
- * Change the label back to what it was.
- */
- SignalLabel = "Start"
- return
-
-
-
- /**
- *
- * Enter a new mail message.
- *
- **/
- EnterMail: interpret "Procedure Expose" GLOBAL
- parse arg comm, dest
-
- if dest = "" then dest = GetCommand("To: ", 1)
- Tmp.Account = dest
- if GetRecord() ~= 1 then do
- say "No such account"
- return
- end
-
- n = CollectMsg(comm)
- if n = 0 then return
-
- call MakeFile(comm, BBSmail"/"CurrentUser.Account, n)
- if Tmp.Account ~= CurrentUser.Account then do
- call MakeFile(comm, BBSmail"/"Tmp.Account, n)
- end
- return
-
-
-
- /**
- *
- * Enter a new message. Someone should build in an editor...
- *
- **/
- EnterMsg : interpret "Procedure Expose" GLOBAL
- parse arg comm
-
- n = CollectMsg(comm)
- if n = 0 then return
-
- call MakeFile(comm, BBSgeneral, n)
- return
-
-
-
- /**
- *
- * An s or not an s
- *
- **/
- Esses: interpret "Procedure Expose" GLOBAL
- arg n
- if n > 1 then return "s"
- return ""
-
-
-
- /**
- *
- * Exit the BBS program
- *
- **/
- ExitBBS: interpret "Procedure Expose" GLOBAL
- parse arg s
- if CurrentUser.Access >= 5 then exit 0
- else call HelpList(s)
- return
-
-
-
- /**
- *
- * This gets the command from stdin. We can't use "pull"
- * because it doesn't echo the way we open things (Fifo doesn't
- * have a console handler) so we have to do it all ourselves (including
- * echo and backspace). No command line editing yet.
- * The first argument is the prompt string, the second argument
- * specifies whether or not to echo what the user types.
- * This routine checks for the presence of a "NO CARRIER"
- * string at the end of the command line. If it is present, the
- * session is aborted immediately. For this to work, you must make sure
- * your modem detects carrier loss and sends this string.
- *
- **/
- GetCommand: interpret "Procedure Expose" GLOBAL
- parse arg pr, echo
- /*
- * Some constants
- */
- cr = '0d'x
- lf = '0a'x
- bs = '08'x
- crlf = cr||lf
-
- call writech("STDOUT", pr)
-
- command = ""
-
- do forever
- /*
- * Read a character from STDIN
- */
- s = readch("STDIN", 1)
- /*
- * If we get an EOF condition, abort this session.
- */
- if eof("STDIN") then SIGNAL "Start"
- /*
- * Echo the character. Watch out for backspaces.
- */
- if echo = 1 then do
- if s ~= bs then call writech("STDOUT", s)
- else if length(command) > 0 then call writech("STDOUT", bs" "bs)
- end
- /*
- * We have a <cr> of <lf>. This is the end of a command line.
- * Echo a line feed to STDOUT. Check if the line ends in
- * NO CARRIER. If so, abort the session. Else, return the command.
- */
- if s = cr | s = lf then do
- call writech("STDOUT", lf)
-
- nc = index(command, "NO CARRIER")
- if nc ~= 0 then do
- if nc = length(command) - 9 then do
- say "NO CARRIER detected, aborting session"
- SIGNAL "Start"
- end
- end
-
- return command
- end
- /*
- * It's a backspace. Take off the last character of the command.
- */
- else if s = bs then do
- l = length(command)
- if l > 0 then command = substr(command, 1, l - 1)
- end
- /*
- * A regular character. Add it to the command
- */
- else command = command || s
- end
- return
-
-
-
- /**
- *
- * Get highest numbered message in the source directory
- *
- **/
- GetHighMsg: interpret "Procedure Expose" GLOBAL
- parse arg source
-
- files = showdir(source, "FILES")
- /*
- * Loop over the files, and get the highest unread message number
- */
- high = 0
- do i = 1
- parse var files "msg."k files
- if k > high then high = k
- if files = "" then leave
- end
- return high
-
-
-
- /**
- *
- * Get number of messages left to read.
- *
- **/
- GetMsgLeft: interpret "Procedure Expose" GLOBAL
- parse arg source, last
-
- files = showdir(source, "FILES")
- /*
- * Loop over the files, and extract number of messages left to read (n)
- */
- n = 0
- do i = 1
- parse var files "msg."k files
- if k > last then n = n + 1
- if files = "" then leave
- end
- return n
-
-
-
-
- /**
- *
- * Retrieve a user's record
- *
- **/
- GetRecord: interpret "Procedure Expose" GLOBAL "Tmp."
- succ = 0
- if open("fi", BBSusers"/"Tmp.Account) then do
- t = readln("fi")
- if t ~= "" then do
- parse var t Tmp.Password '|' Tmp.Access '|' ,
- Tmp.Name '|' Tmp.Address '|' ,
- Tmp.City '|' Tmp.Country '|' ,
- Tmp.Telephone '|' Tmp.MsgCount '|' ,
- Tmp.MailCount '|' Tmp.Protocol '|'
- succ = 1
- end
- call close("fi")
- end
- return succ
-
-
-
- /**
- *
- * This gets a yes/no decision from stdin
- * The single argument is used as the prompt.
- *
- **/
- GetYesNo: interpret "Procedure Expose" GLOBAL
- parse arg prompt
-
- do i = 1
- ss = upper(GetCommand(prompt" [Yes/No]: ", 1))
- if substr(ss, 1, 1) = 'Y' then return 1
- else if substr(ss, 1, 1) = 'N' then return 0
- else do
- say "A Yes or No was expected, retry"
- end
- end
- return
-
-
-
- /**
- *
- * List supported commands. Can be as extensive as you want.
- *
- **/
- HelpList: interpret "Procedure Expose" GLOBAL
- parse arg s
-
- if s ~= "" then say "Unknown command: "s
-
- Say "Supported commands are: "
- Say "-------------------------+-------------------------------------"
- Say "DOWNLOAD [filename] | Download a file [called filename]"
- Say "ENTER | Enter a message"
-
- if CurrentUser.Access >= 5 then
- Say "*EXIT | Exit the BBS program"
-
- Say "HELP | Display this list"
- Say "LIST | List downloadable files"
- Say "LOGOFF | Logoff"
- Say "MAIL | Go to mail subsytem"
- Say "PASSWORD | Set new password"
- Say "PROTOCOL [n] | Set new transfer protocol [to n]"
- Say "READ [message] | Read messages [starting at message]"
-
- if CurrentUser.Access >= 5 then
- Say "*REGISTER | Add a new user to the system"
-
- Say "SHOW [name] | Show current record [of user ""name""]"
-
- if CurrentUser.Access >= 5 then
- Say "*SYSTEM | Change to system command level"
-
- Say "UPLOAD [filename] | Upload a file [called filename]"
- Say "USERS | Show the user list"
-
- if CurrentUser.Access >= 5 then
- Say "*VALIDATE [user] [level] | Validate a new user"
- Say "-------------------------+-------------------------------------"
- return
-
-
-
- /**
- *
- * List supported commands in mail.
- *
- **/
- HelpLMail: interpret "Procedure Expose" GLOBAL
- parse arg s
-
- if s ~= "" then say "Unknown command: "s
-
- Say "Supported commands while in mail are: "
- Say "----------------+-------------------------------------"
- Say "TO | Enter a message"
- Say "HELP | Display this list"
- Say "QUIT | Quit from the mail subsystem"
- Say "READ [message] | Read messages [starting at message]"
- Say "SHOW [name] | Show current record [of user ""name""]"
- Say "USERS | Show the user list"
- Say "----------------+-------------------------------------"
- return
-
-
-
- /**
- *
- * List downloadable files
- *
- **/
- ListFiles: interpret "Procedure Expose" GLOBAL
- address command "list "BBSlistings" nohead"
- return
-
-
-
- /**
- *
- * Handle logins and new registrations.
- * Argument is a user account name, so we can log ourselves back in
- * if we as a sysop have added someone else using Register().
- *
- **/
- Login: interpret "Procedure Expose" GLOBAL
- Tmp. = ""
- Tmp.Access = 0
- CurrentUser.Access = 0
-
- Tmp.Account = upper(GetCommand("Username: ", 1))
- if Tmp.Account = "NEW" then do
- call Register("")
- return
- end
- else if GetRecord() = 0 then do
- say "Not registered."
- say "To register, use the NEW account."
- end
- else do
- s = upper(GetCommand("Password: ", 0))
- if s ~= Tmp.Password then do
- say "Unauthorized."
- say "Bye now..."
- Tmp.Access = 0
- end
- end
-
- call CopyRecord()
- if CurrentUser.Access = 2 then say "You are not yet validated"
- return
-
-
-
- /**
- *
- * Make a file header, and add it in the destination directory
- *
- **/
- MakeFile: interpret "Procedure Expose" GLOBAL "msg."
- parse arg comm, dest, nlins
- /*
- * Get list of files.
- */
- files = showdir(dest, "FILES")
- /*
- * Loop over the files, extract the highest message number and add
- * 1 for the current message.
- */
- high = 0
- do i = 1
- parse var files "msg."k files
- if k > high then high = k
- if files = "" then leave
- end
- high = high + 1
- /*
- * Header
- */
- msg.0 = "=========="
- msg.1 = "# "high", "date()", "time()", from "CurrentUser.Account". "
- if comm ~= "" then msg.1 = msg.1 || "Comment to "comm"."
- msg.2 = "----------"
-
- if ~open("fo", dest"/msg."high, "W") then do
- say "Cannot add a message right now"
- return
- end
-
- do i = 0 to nlins
- call writeln("fo", msg.i)
- end
-
- call close("fo")
- return
-
-
-
- /**
- *
- * Read mail messages.
- * One argument: the message number to start reading. This resets the
- * message pointer. This also allows you to skip to the last.
- *
- **/
- ReadMail: interpret "Procedure Expose" GLOBAL
- parse arg nm
- /*
- * If we have a message number for argument set user's message pointer
- * to just before that.
- */
- if nm ~= "" then CurrentUser.MailCount = nm - 1
- /*
- * Unread mail
- */
- source = BBSmail"/"CurrentUser.Account
-
- n = GetMsgLeft(source, CurrentUser.MailCount)
- if n ~= 0 then say "You have "n" unread mail message"Esses(n)
- else CurrentUser.MailCount = GetHighMsg(source)
- /*
- * Message read loop
- */
- do i = 1 to n
- do k = CurrentUser.MailCount + 1
- if ~exists(source"/msg."k) then iterate k
- address command "type "source"/msg."k
- CurrentUser.MailCount = k
-
- do j = 1
- s = upper(GetCommand("[Quit, Again, Delete, Reply, Next = <cr>]: ", 1))
- if abbrev("QUIT", s, 1) then return
- else if abbrev("AGAIN", s, 1) then do
- CurrentUser.MailCount = k - 1
- i = i - 1
- end
- else if abbrev("DELETE", s, 1) then do
- call Delete(source"/msg."k)
- say "Deleted"
- CurrentUser.MailCount = k - 1
- end
- else if abbrev("REPLY", s, 1) then do
- if open("fi", source"/msg."k) then do
- call readln("fi")
- t = readln("fi")
- parse var t dummy "from " owner ". " rest
- call close("fi")
- call EnterMail(k, owner)
- end
- end
- else if abbrev("NEXT", s, 1) then nop
- else if s = "" then nop
- else iterate j
- iterate i
- end
- end
- end
- say "No more unread messages"
- return
-
-
-
- /**
- *
- * Read messages.
- * Two arguments: (1) the message number to start reading. This resets the
- * message pointer. This also allows you to skip to the last. (2) The
- * source directory to read from.
- *
- **/
- ReadMsg : interpret "Procedure Expose" GLOBAL
- parse arg nm
- /*
- * If we have a message number for argument set user's message pointer
- * to just before that.
- */
- if nm ~= "" then CurrentUser.MsgCount = nm - 1
- /*
- * Unread regular messages
- */
- source = BBSgeneral
-
- n = GetMsgLeft(source, CurrentUser.MsgCount)
- if n ~= 0 then say "You have "n" unread general message"Esses(n)
- else CurrentUser.MsgCount = GetHighMsg(source)
- /*
- * Message read loop
- */
- do i = 1 to n
- do k = CurrentUser.MsgCount + 1
- if ~exists(source"/msg."k) then iterate k
- address command "type "source"/msg."k
- CurrentUser.MsgCount = k
-
- do j = 1
- s = upper(GetCommand("[Quit, Again, Delete, Comment, Next = <cr>]: ", 1))
-
- if abbrev("QUIT", s, 1) then return
- else if abbrev("AGAIN", s, 1) then do
- CurrentUser.MsgCount = k - 1
- i = i - 1
- end
- else if abbrev("DELETE", s, 1) then do
- if open("fi", source"/msg."k) then do
- call readln("fi")
- t = readln("fi")
- parse var t dummy "from " owner ". " rest
- call close("fi")
- if owner = CurrentUser.Account then do
- call Delete(source"/msg."k)
- say "Deleted"
- end
- else do
- say "You didn't write this message"
- if CurrentUser.Access >= 5 then do
- if GetYesNo("Withdraw anyway? ") = 1 then do
- call Delete(source"/msg."k)
- say "Deleted"
- end
- end
- end
- end
- end
- else if abbrev("COMMENT", s, 1) then call EnterMsg(k, source)
- else if abbrev("NEXT", s, 1) then nop
- else if s = "" then nop
- else iterate j
- iterate i
- end
- end
- end
- say "No more unread messages"
- return
-
-
-
- /**
- *
- * Register a new user. The new user is immediately added to the
- * system, but his access code is 2 which doesn't allow her to
- * log in yet. The Sysop uses the Validate command to set the access
- * code to a higher level. 3 is suggested... 5 gives system privileges.
- *
- **/
- Register: interpret "Procedure Expose" GLOBAL
- parse arg s
- /*
- * If access = 0 this is a new user. If access = 5, this is called by
- * the Sysop.
- */
- if CurrentUser.Access = 0 then prefix = "Your "
- else if CurrentUser.Access < 5 then do
- call HelpList(s)
- return
- end
- else prefix = "New "
- /*
- * Generate registration record
- */
- Tmp.Account = GetCommand(prefix"account name: ", 1)
- if GetRecord() = 1 then do
- say "Account name already taken"
- return
- end
- Tmp.Password = upper(GetCommand(prefix"password: ", 0))
- Tmp.Name = GetCommand(prefix"full name: ", 1)
- Tmp.Address = GetCommand(prefix"address: ", 1)
- Tmp.City = GetCommand(prefix"city, zip: ", 1)
- Tmp.Country = GetCommand(prefix"country and/or state: ", 1)
- Tmp.Telephone = GetCommand(prefix"telephone number: ", 1)
- Tmp.Protocol = 1
- Tmp.Access = 2
- Tmp.MsgCount = 0
- Tmp.MailCount = 0
-
- say "You are:"
- say Tmp.Name
- say Tmp.Address
- say Tmp.City
- say Tmp.Country
- say Tmp.Telephone
-
- if GetYesNo("Correct? ") = 1 then do
- call SetRecord()
-
- if CurrentUser.Access = 0 then do
- say "Please give the Sysop a chance to validate you (usually < 24 hours)."
- say "Thank you for registering with this BBS."
- end
- end
- return
-
-
-
- /**
- *
- * Change a user's record
- *
- **/
- SetRecord: interpret "Procedure Expose" GLOBAL "Tmp."
- if Tmp.Access ~= 0 then do
- t = Tmp.Password || '|' || Tmp.Access || '|' ||,
- Tmp.Name || '|' || Tmp.Address || '|' ||,
- Tmp.City || '|' || Tmp.Country || '|' ||,
- Tmp.Telephone || '|' || Tmp.MsgCount || '|' ||,
- Tmp.MailCount || '|' || Tmp.Protocol || '|'
- if open("fo", BBSusers"/"Tmp.Account, "W") then do
- call writeln("fo", t)
- call close("fo")
- end
- end
- else call Delete(BBSusers'/'Tmp.Account)
- return
-
-
-
- /**
- *
- * Show a user's stats.
- *
- **/
- ShowRecord: interpret "Procedure Expose" GLOBAL
- arg username
-
- if username = "" then Tmp.Account = CurrentUser.Account
- else Tmp.Account = username
- if GetRecord() = 1 then do
- say "Account info for "Tmp.Account":"
- say Tmp.Name
- say Tmp.Address
- say Tmp.City
- say Tmp.Country
- say Tmp.Telephone
- /*
- * If asking about another user, don't need to show protocol.
- * If asking about ourselves, then show current protocol, not "saved" one.
- */
- if username = "" then do
- i = CurrentUser.Protocol + 0
- say "Transfer protocol: "Protocols.i.nam
- end
- end
- else say "User "username" not found"
- return
-
-
-
- /**
- *
- * List files
- *
- **/
- ShowUsers: interpret "Procedure Expose" GLOBAL
- address command "list "BBSusers" nohead quick"
- return
-
-
-
- /**
- *
- * Upload a new file
- *
- **/
- UpLoad : interpret "Procedure Expose" GLOBAL
- parse arg filnam
-
- if filnam = "" then filnam = GetCommand("File name? ", 1)
- if exists(BBSlistings"/"filnam) then do
- say filnam" already exists!"
- return
- end
-
- say "Now send file "filnam
-
- proto = CurrentUser.Protocol + 0
- address VLT "transfer protocol external; transfer mode image"
- address VLT "xpr select "Protocols.proto.lib
- address VLT "CD "BBSlistings
- if Protocols.proto.set ~= "" then address VLT "xpr init "Protocols.proto.set
- address VLT "file receive "BBSlistings"/"filnam
- /*
- * Switch back to XMODEM protocol so that we can't automatically start
- * receiving stuff.
- */
- address VLT "transfer protocol XMODEM"
-
- return
-
-
-
- /**
- *
- * Routine to validate FifoBBS users. Only callable by the Sysop.
- *
- **/
- Validate: interpret "Procedure Expose" GLOBAL
- parse arg s, nam, acc .
-
- if CurrentUser.Access < 5 then do
- call HelpList(s)
- return
- end
-
- if nam = "" then Tmp.Account = GetCommand("Name: ", 1)
- else Tmp.Account = nam
-
- if GetRecord() = 0 then do
- say "Unknown account"
- return
- end
-
- if ~exists(BBSmail"/"Tmp.Account) then call Makedir(BBSmail"/"Tmp.Account)
-
- if acc = "" then do
- say "Account info for "Tmp.Account":"
- say Tmp.Name
- say Tmp.Address
- say Tmp.City
- say Tmp.Country
- say Tmp.Telephone
- say "Transfer protocol: "Tmp.Protocol
- say "Access code: "Tmp.Access
-
- if GetYesNo("Change access code? ") = 1 then do
- Tmp.Access = GetCommand("Enter new access code: ", 1)
- call SetRecord()
- end
- end
- else do
- Tmp.Access = acc
- call SetRecord()
- end
- return
-
-
-
-
-
- BREAK_C:
- BREAK_D:
- BREAK_E:
- BREAK_F:
- ERROR:
- FAILURE:
- HALT:
- IOERROR:
- NOVALUE:
- SYNTAX:
- say "Command returned with error"
-
- interpret "SIGNAL" SignalLabel
-
- exit 0
-